package ws.rest.check.validator;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;

import ws.rest.check.exception.CheckResourceNotValidException;

/**
 * 解析check.xml
 * 
 */
public class CheckResouceParser {

	public final static String TPL_VAR_FLG = "#";
	
	private static Validator validator;
	
	static {
		
		//获得根路径
		String currDir = DataChecker.class.getClassLoader().getResource("")
				.getPath();
		File schemaFile = new File(currDir + "/check-rules.xsd");
		SchemaFactory schemaFactory = SchemaFactory
				.newInstance("http://www.w3.org/2001/XMLSchema");
		try {
			Schema schema = schemaFactory.newSchema(schemaFile);
			validator = schema.newValidator();
		} catch (SAXException e) {
			e.printStackTrace();
			validator = null;
		}
	}

	public List<CheckItem> parseCheckResource(String checkResource)
			throws DocumentException, CheckResourceNotValidException, SAXException {
		StringReader sr = new StringReader(checkResource.trim());
		Source source = new StreamSource(sr);

		try {
			validator.validate(source);
		} catch (SAXException e) {
			e.printStackTrace();
			throw e;
		} catch (IOException e) {
			e.printStackTrace();
			throw new CheckResourceNotValidException("");
		} 

		ArrayList<CheckItem> items = new ArrayList<CheckItem>();

		SAXReader reader = new SAXReader();

		StringReader strReader = new StringReader(checkResource);

		Document document = reader.read(strReader);

		Element root = document.getRootElement();

		List<?> itemElems = root.elements("item");

		// 获取所有item元素
		for (Iterator<?> itemsIt = itemElems.iterator(); itemsIt.hasNext();) {
			Element itemElem = (Element) itemsIt.next();

			String itemName = itemElem.attributeValue("name");
			String errMsg = null;
			List<Rule> itemRules = new ArrayList<Rule>();

			// 获取出错信息
			Element errMsgElem = itemElem.element("errmsg");
			if (null != errMsgElem) {
				errMsg = errMsgElem.getTextTrim();
			}

			// 获得验证规则
			List<?> itemRuleNodes = itemElem.elements();
			for (Iterator<?> itemRulesIt = itemRuleNodes.iterator(); itemRulesIt
					.hasNext();) {
				Element ruleElem = (Element) itemRulesIt.next();

				String ruleName = ruleElem.getName();
				String ruleErrMsg = null;
				Map<String, String> ruleAttrs = new HashMap<String, String>();
				ArrayList<Object> ruleCond = null;

				// 跳过errmsg节点
				if (false == ruleElem.getName().equalsIgnoreCase("errmsg")) {

					// 获得错误信息
					Element ruleErrMsgElem = ruleElem.element("errmsg");
					if (null != ruleErrMsgElem) {
						ruleErrMsg = ruleErrMsgElem.getTextTrim();
					}

					// 获取属性
					int ruleAtrrNum = ruleElem.attributeCount();
					if (0 != ruleAtrrNum) {
						for (int i = 0; i < ruleAtrrNum; i++) {
							Attribute aAttr = ruleElem.attribute(i);
							ruleAttrs.put(aAttr.getName(), aAttr
									.getStringValue());
						}
					}

					// 解析when元素下内容，parseConditionElement
					Element ruleCondElem = ruleElem.element("when");
					ruleCond = this.parseConditionElement(ruleCondElem);

					// 创建Rule对象
					itemRules.add(new Rule(ruleName, ruleCond, ruleErrMsg,
							ruleAttrs));
				}

			}

			CheckItem checkItem = new CheckItem(itemName, itemRules,
					errMsg);

			if (null != itemElem.attributeValue("inLoop")) {
				checkItem.setInLoop(true);
			}

			if (null != itemElem.attributeValue("bound")) {
				checkItem.setBound(true);
			}

			if (null != itemElem.attributeValue("byPattern")) {
				checkItem.setByPattern(true);
			}

			items.add(checkItem);
		}

		return items;
	}

	/**
	 * 解析条件结构（递归）
	 * 
	 * <and>(LinkedHashMap<and,List>) <boolexpr... /> (BoolExpresion) <or>
	 * (LinkedHashMap) <boolexpr... /> (BoolExpresion) </or> </and>
	 * 
	 * @param condElem
	 * @return
	 */
	private ArrayList<Object> parseConditionElement(Element condElem) {
		ArrayList<Object> cond = new ArrayList<Object>();

		if (null != condElem) {
			List<?> subCondElems = condElem.elements();
			for (Iterator<?> subCondElemsIt = subCondElems.iterator(); subCondElemsIt
					.hasNext();) {
				Element aSubCondElem = (Element) subCondElemsIt.next();
				// 对于and ，or处理，用Map进行封装
				if (aSubCondElem.getName().equalsIgnoreCase("and")
						|| aSubCondElem.getName().equalsIgnoreCase("or")) {
					Map<String, Object> logicBody = new LinkedHashMap<String, Object>();
					// and或者or递归处理
					logicBody.put(aSubCondElem.getName(), this
							.parseConditionElement(aSubCondElem));

					cond.add(logicBody);
				} else if (aSubCondElem.getName().equalsIgnoreCase("boolexpr")) {
					String left = aSubCondElem.attributeValue("left");
					String operator = aSubCondElem.attributeValue("op");
					String right = aSubCondElem.attributeValue("right");
					//用boolExpression进行封装
					BoolExpresion aExpr = new BoolExpresion(left, operator,
							right);

					if (null != aSubCondElem.attributeValue("leftAtOuter")
							&& "true" == aSubCondElem
									.attributeValue("leftAtOuter")) {
						aExpr.setLeftAtOuter(true);
					}

					if (null != aSubCondElem.attributeValue("rightAtOuter")
							&& "true" == aSubCondElem
									.attributeValue("rightAtOuter")) {
						aExpr.setRightAtOuter(true);
					}
					cond.add(aExpr);
				}

			}
		}

		return cond;
	}

	/**
	 * 将 check.xml 转成前端可识别的Json格式。
	 * 
	 * @param checkItems
	 * @return
	 */
	public static String validResourceToJs(List<CheckItem> checkItems) {

		StringBuffer sb = new StringBuffer();
		sb.append("<script type=\"text/javascript\">");
		sb.append("var Validator_JSON = {");

		for (CheckItem item : checkItems) {

			// 联合校验类型用追加#B
			if (item.isBound()) {
				sb.append(" '" + item.getItemName() + "#B' : { ");
			} else {
				sb.append(" '" + item.getItemName() + "' : { ");
			}

			if (item.isInLoop()) {
				sb.append("inLoop : true ,");
			}
			/*
			 * if (item.isBound()) { sb.append("bound : true ,"); }
			 */
			if (item.isByPattern()) {
				sb.append("byPattern : true ,");
			}

			for (Rule rule : item.getRules()) {
				sb.append(rule.getName() + " : {");

				Map<String, String> attrs = rule.getAttributes();

				if (null != attrs) {
					if ("custom".equals(rule.getName())) {
						sb.append("validatorName : '"
								+ attrs.get("validatorName") + "' ,");
						sb.append("params : {");

						for (Object key : attrs.keySet()) {
							if (true == "custom".equals(rule.getName())
									&& false == "validatorName".equals(key)) {
								sb.append(key + " : '" + attrs.get(key) + "',");
							}
						}
						sb.append("},");
					} else {
						for (Object key : attrs.keySet()) {
							sb.append(key + " : '" + attrs.get(key) + "',");
						}
					}
				}
				// 条件
				List<Object> cond = rule.getCondition();

				if (null != cond && 0 != cond.size()) {
					sb.append("condition : '");
					sb.append(convtCondToJs(cond.get(0), item.isInLoop()));
					sb.append("', ");
				}

				sb.append("errmsg : '" + rule.getErrMsg() + "'");
				sb.append(" }, ");
			}

			sb.append(" },");
		}

		sb.append("};");
		sb.append("</script>");

		return sb.toString().replaceAll(",(\\s)*}", "}");
	}

	/**
	 * 转换条件表达式到js
	 * 
	 * @return
	 */
	private static String convtCondToJs(Object aCond, boolean isInLoop) {

		if (null == aCond) {
			return "";
		}

		StringBuffer sb = new StringBuffer();

		// 如果布尔表达式
		if (aCond instanceof BoolExpresion) {
			BoolExpresion theBoolExpr = (BoolExpresion) aCond;

			if (null != theBoolExpr.getOperator()
					&& null != theBoolExpr.getLeft()) {

				boolean leftIsAVar = isVar(theBoolExpr.getLeft());
				boolean rightIsAVar = isVar(theBoolExpr.getRight());

				sb.append(" ");
				// 处理左值
				if (true == leftIsAVar) {
					sb.append(theBoolExpr.getLeft());
					/*
					 * if (true == isInLoop && false ==
					 * theBoolExpr.isLeftAtOuter()) { sb.append("#N"); }
					 */

				} else {
					//循环体变量
					if (theBoolExpr.getLeft().trim().startsWith("{")
							&& theBoolExpr.getLeft().trim().endsWith("}")) {
						sb.append(" " + theBoolExpr.getLeft() + " ");
					}
					//常量
					else {
						sb.append(" \"" + theBoolExpr.getLeft() + "\" ");
					}
				}

				// 处理操作符
				if (true == leftIsAVar
						&& "checked".equals(theBoolExpr.getOperator())) {
					sb.append(".checked ");
				} else {
					sb.append(" " + theBoolExpr.getJsOP() + " ");
				}

				// 处理右值
				// 如果不是checked类型，右值才有效
				/*
				 * if (false == "checked".equals(theBoolExpr.getOperator())) {
				 * // 变量 if (true == rightIsAVar) { sb.append(" " +
				 * theBoolExpr.getRight() + " "); } // 常量 else { sb.append(" \""
				 * + theBoolExpr.getRight() + "\" "); } }
				 */

				if (true == rightIsAVar) {
					sb.append(" " + theBoolExpr.getRight() + " ");
				}
				else {
					//循环体变量
					if (theBoolExpr.getRight().trim().startsWith("{")
							&& theBoolExpr.getRight().trim().endsWith("}")) {
						sb.append(" " + theBoolExpr.getRight() + " ");
					} 
					// 常量
					else {
						sb.append(" \"" + theBoolExpr.getRight() + "\" ");
					}
				}

				sb.append(" ");
			}

		} else if (aCond instanceof Map<?, ?>) {
			boolean isAnd = true;

			Object andOr = ((Map<?, ?>) aCond).get("and");

			if (null == andOr) {
				andOr = ((Map<?, ?>) aCond).get("or");
				isAnd = false;
			}

			if (null != andOr && andOr instanceof List<?>) {
				sb.append(" (");

				List<?> theSubCondList = (List<?>) andOr;
				int idx = 1;
				for (Object cond : theSubCondList) {
					if (1 != idx) {
						if (true == isAnd) {
							sb.append(" && ");
						} else {
							sb.append(" || ");
						}
					}
					sb.append(convtCondToJs(cond, isInLoop));
					idx++;
				}

				sb.append(") ");
			}
		}

		return sb.toString();
	}

	/**
	 * 判断是否是变量
	 * 
	 * @param mayBeAVar
	 * @return
	 */
	private static boolean isVar(String mayBeAVar) {
		if (null != mayBeAVar) {
			if (mayBeAVar.trim().startsWith(TPL_VAR_FLG)) {
				return true;
			}
		}

		return false;
	}
}